use actix_session::Session;
use actix_web::{Responder, Result, post, web};

use crate::core::{
    common::AppError,
    passwd::{Passwd, TeacherOrStudent},
    student::Student,
    teacher::Teacher,
    token_manage::{self, Claims},
};

const DEFAULT_PASSWD: &str = "pass01!";
#[post("/logon")]
pub async fn logon(user: web::Json<LogonModel>,session:Session) -> Result<impl Responder, AppError> {
    let (uid, passwd) = match user.flag {
        TeacherOrStudent::Teacher => {
            let teacher = Teacher::find_by_name(&user.name).await?;
            if teacher.is_none() {
                return Ok(web::Json(LogonResModel::failed("没有该用户".into())));
            }
            let uid = teacher.as_ref().unwrap().id;
            let passwd = Passwd::get_passwd_by_uid(uid, TeacherOrStudent::Teacher).await?;
            (uid, passwd)
        }
        TeacherOrStudent::Student => {
            let stu = Student::find_by_name(&user.name).await?;
            if stu.is_none() {
                return Ok(web::Json(LogonResModel::failed("没有该用户".into())));
            }
            let uid = stu.as_ref().unwrap().id;
            let passed = Passwd::get_passwd_by_uid(uid, TeacherOrStudent::Student).await?;
            (uid, passed)
        }
    };
    let pwd = if passwd.is_none() {
        let passwd= Passwd::new(uid, DEFAULT_PASSWD.to_string(), user.flag);
        match passwd.add_passwd().await{
            Err(e)=>{
                log::error!("write passwd to db failed! {}",e);
            },
            _=>{}
        }
        DEFAULT_PASSWD
    } else {
        &passwd.unwrap().password
    };
    if user.pwd.eq(pwd) {
        let claims = Claims::new(uid, user.flag);
        let token = token_manage::generate_jwt(&claims)?;
        session.insert("uid", uid)?;
        session.insert("flag",Into::<u8>::into(user.flag))?;
        return Ok(web::Json(LogonResModel::success(token)));
    } else {
        return Ok(web::Json(LogonResModel::failed("密码不正确".into())));
    }
}

#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct LogonModel {
    name: String,
    pwd: String,
    flag: TeacherOrStudent,
}

#[derive(serde::Serialize,serde::Deserialize)]
pub struct LogonResModel {
    result: bool,
    msg: Option<String>,
    data: Option<String>,
}

impl LogonResModel {
    fn success(token: String) -> Self {
        Self {
            result: true,
            msg: None,
            data: Some(token),
        }
    }
    fn failed(msg: String) -> Self {
        Self {
            result: false,
            msg: Some(msg),
            data: None,
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use actix_web::{App, http::header::ContentType, test};

    #[actix_web::test]
    async fn test_logon_ok() {
        let req_mode = LogonModel {
            name: "张三".into(),
            pwd: "pass01!".into(),
            flag: TeacherOrStudent::Student,
        };
        let app = test::init_service({ App::new().service(logon) }).await;
        let req_mode_str = serde_json::to_string(&req_mode).unwrap();
        let req = test::TestRequest::post()
            .uri("/logon")
            .insert_header(ContentType::json())
            .set_payload(req_mode_str.into_bytes())
            // .app_data(req_mode_str)
            .to_request();
        let resp:LogonResModel = test::call_and_read_body_json(&app, req).await;
        // assert_eq!(resp.status(),200);
        assert_eq!(resp.result,true);
        assert!(resp.data.is_some());
        println!("{}",resp.data.unwrap());
    }
}
